<?php

/**
 * 安心站长axzz.cn，一个追求技术创造者。让网站、小程序、APP、应用程序牛起来……
 *
 * cighsen02 365182575
 *
 * Create at: 2021.08.21
 * Update at: 2025.09.15
 */

namespace Twoir;

use Config;
use ErrorException;
use Throwable;

use function defined;
use function error_reporting;
use function header;
use function json_encode;
use function set_error_handler;
use function set_exception_handler;
use function sprintf;
use function strlen;
use function substr;

/**
 * 系统错误|异常 处理
 */
class ErrorHandler
{
    protected string $statusText;

    protected int $statusCode;

    protected string $version;

    public static $statusTexts = [
        100 => 'Continue',
        101 => 'Switching Protocols',
        102 => 'Processing',            // RFC2518
        103 => 'Early Hints',
        200 => 'OK',
        201 => 'Created',
        202 => 'Accepted',
        203 => 'Non-Authoritative Information',
        204 => 'No Content',
        205 => 'Reset Content',
        206 => 'Partial Content',
        207 => 'Multi-Status',          // RFC4918
        208 => 'Already Reported',      // RFC5842
        226 => 'IM Used',               // RFC3229
        300 => 'Multiple Choices',
        301 => 'Moved Permanently',
        302 => 'Found',
        303 => 'See Other',
        304 => 'Not Modified',
        305 => 'Use Proxy',
        307 => 'Temporary Redirect',
        308 => 'Permanent Redirect',    // RFC7238
        400 => 'Bad Request',
        401 => 'Unauthorized',
        402 => 'Payment Required',
        403 => 'Forbidden',
        404 => 'Not Found',
        405 => 'Method Not Allowed',
        406 => 'Not Acceptable',
        407 => 'Proxy Authentication Required',
        408 => 'Request Timeout',
        409 => 'Conflict',
        410 => 'Gone',
        411 => 'Length Required',
        412 => 'Precondition Failed',
        413 => 'Payload Too Large',
        414 => 'URI Too Long',
        415 => 'Unsupported Media Type',
        416 => 'Range Not Satisfiable',
        417 => 'Expectation Failed',
        418 => 'I\'m a teapot',                                               // RFC2324
        421 => 'Misdirected Request',                                         // RFC7540
        422 => 'Unprocessable Entity',                                        // RFC4918
        423 => 'Locked',                                                      // RFC4918
        424 => 'Failed Dependency',                                           // RFC4918
        425 => 'Too Early',                                                   // RFC-ietf-httpbis-replay-04
        426 => 'Upgrade Required',                                            // RFC2817
        428 => 'Precondition Required',                                       // RFC6585
        429 => 'Too Many Requests',                                           // RFC6585
        431 => 'Request Header Fields Too Large',                             // RFC6585
        451 => 'Unavailable For Legal Reasons',                               // RFC7725
        500 => 'Internal Server Error',
        501 => 'Not Implemented',
        502 => 'Bad Gateway',
        503 => 'Service Unavailable',
        504 => 'Gateway Timeout',
        505 => 'HTTP Version Not Supported',
        506 => 'Variant Also Negotiates',                                     // RFC2295
        507 => 'Insufficient Storage',                                        // RFC4918
        508 => 'Loop Detected',                                               // RFC5842
        510 => 'Not Extended',                                                // RFC2774
        511 => 'Network Authentication Required',                             // RFC6585
    ];

    public function __construct()
    {
        $this->statusCode   = 200;
        $this->statusText 	= static::$statusTexts[$this->statusCode];
        $this->version		= 'HTTP/1.0' !== $_SERVER['SERVER_PROTOCOL'] ? '1.1' : '1.0';

        // 系统错误
        set_error_handler([$this, 'errorHandler']);
        // 异常
        set_exception_handler([$this, 'exceptionHandler']);
    }

    /**
     * 错误处理
     *
     * @param int $severity
     * @param string $message
     * @param string $file
     * @param int $line
     * @return void
     *
     * @throws ErrorException
     */
    public function errorHandler(int $severity, string $message, string $file, int $line): void
    {
        if (error_reporting() & $severity) {

            // 将错误信息托管至异常类
            throw new ErrorException($message, 0, $severity, $file, $line);
        }
    }

    /**
     * 异常处理
     *
     * @param Throwable $exception
     * @param int $status
     * @return void
     */
    public function exceptionHandler(Throwable $exception, int $status = 200): void
    {
        if(1000 < $code = $exception->getCode()) {

            $status             = (int)substr($code, -3);
            $this->statusText   = static::$statusTexts[$status] ?? '';
            $this->statusCode   = (int)substr($code, 0, strlen($code) - 3);
        }
        elseif (204 === $code) {

            $status = $code;
        }

        header(sprintf('Content-type: %s; %s', defined('IS_API_ENABLED') && IS_API_ENABLED ? 'application/json' : 'text/html', 'charset=UTF-8'));
        header(sprintf('HTTP/%s %s %s', $this->version, $status, $this->statusText), true, $status);

        // 开始调试,并格式化
        if (Config::twoir('api.debug')) {

            $data = $exception;
        }
        // json显示
        elseif(defined('IS_API_ENABLED') && IS_API_ENABLED){

            $data = json_encode([
                'code'       => $this->statusCode,
                'message'    => $exception->getMessage()
            ], 15);
        }
        // 正常显示，可以使用模板
        else {

            $data = $exception->getMessage();
        }

        echo $data;
    }
}
